﻿using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;

namespace Leow.TextEditor
{
    /// <summary>
    /// Contains useful custom-created extension methods.
    /// </summary>
    internal static class CustomExtensionMethods
    {
        private static Regex _lineTextRegex;

        /// <summary>
        /// Gets the singleton instance of the regex that can be used for
        /// matching texts line-by-line.
        /// </summary>
        private static Regex LineTextRegex
        {
            get
            {
                return _lineTextRegex ??
                       (_lineTextRegex
                        = new Regex(@"[^\r\n]*(?:\r\n|\n|\r)|[^\r\n]+$", RegexOptions.Compiled));
            }
        }

        /// <summary>
        /// Gets the enclosing rectangular bounds of a <c>UIElement</c>, in
        /// relative to another <c>UIElement</c>'s location.
        /// </summary>
        /// <param name="source">A <c>UIElement</c> whose bounds is to be
        /// determined.</param>
        /// <param name="element">A <c>UIElement</c> to be designated as the
        /// point of reference.</param>
        /// <returns>A <c>Rect</c> structure that contains the bounds of a
        /// <c>UIElement</c>, in relative to another specified <c>UIElement</c>.
        /// </returns>
        public static Rect GetBoundsRelativeTo(
            this UIElement source,
            UIElement element)
        {
            Point topLeftPoint = source.TranslatePoint(new Point(), element);
            return new Rect(
                topLeftPoint.X,
                topLeftPoint.Y,
                source.RenderSize.Width,
                source.RenderSize.Height);
        }

        /// <summary>
        /// Gets the first child UI element of a specified source UI element
        /// that matches the specified UI element type. This child UI element
        /// is located by traversing the visual tree.
        /// </summary>
        /// <typeparam name="T">The type of child UI element expected.
        /// </typeparam>
        /// <param name="source">A UI element in which to look for the child UI
        /// element.</param>
        /// <param name="isSearchRecursively"><c>true</c> to recursively look
        /// within children of children until first such UI element is found;
        /// <c>false</c> to look within the source's direct children only. The
        /// default value is <c>true</c>.</param>
        /// <returns>The first child UI element of the source that is of type T,
        /// or <c>null</c> if there are no such child UI element.</returns>
        public static T GetFirstVisualChildOfType<T>(
            this DependencyObject source,
            bool isSearchRecursively = true)
            where T : DependencyObject
        {
            int childrenCount = VisualTreeHelper.GetChildrenCount(source);
            var children = Enumerable.Range(0, childrenCount)
                .Select(i => VisualTreeHelper.GetChild(source, i));
            var childOfTypeT = children
                .OfType<T>()
                .FirstOrDefault();
            if (childOfTypeT != null)
            {
                return childOfTypeT;
            }

            if (isSearchRecursively)
            {
                var firstChildOfTypeT = children
                    .Select(c => c.GetFirstVisualChildOfType<T>())
                    .FirstOrDefault(c => c != null);
                return firstChildOfTypeT;
            }

            return null;
        }

        /// <summary>
        /// Gets the first parent UI element of a specified source UI element
        /// that matches the specified UI element type. This parent UI element
        /// is located by traversing the visual tree.
        /// </summary>
        /// <typeparam name="T">The type of parent UI element expected.
        /// </typeparam>
        /// <param name="source">A UI element in which to look for the parent UI
        /// element.</param>
        /// <returns>The first parent UI element of the source that is of type
        /// T, or <c>null</c> if there are no such parent UI element.</returns>
        public static T GetFirstVisualParentOfType<T>(this DependencyObject source)
            where T : DependencyObject
        {
            if (source == null)
            {
                return null;
            }

            DependencyObject parent = VisualTreeHelper.GetParent(source);
            if (parent is T)
            {
                return parent as T;
            }

            return parent
                .GetFirstVisualParentOfType<T>();
        }

        /// <summary>
        /// <para>Inverts the given color and returns a new color that is the
        /// result of the inversion.</para>
        /// <para>The alpha (transparency) component of the color is not
        /// inverted.</para>
        /// </summary>
        /// <param name="source">The color to be inverted.</param>
        /// <returns>The inverted color.</returns>
        public static Color Invert(this Color source)
        {
            var invertedColor = Color.FromRgb(
                (byte)~source.R,
                (byte)~source.G,
                (byte)~source.B);
            return invertedColor;
        }

        /// <summary>
        /// Returns a list of strings, each representing the individual lines
        /// from the source text.
        /// </summary>
        /// <param name="source">The source text.</param>
        /// <returns>A list of strings, each representing the individual lines
        /// from the source text.</returns>
        public static IEnumerable<string> ToLines(this string source)
        {
            var lines = LineTextRegex.Matches(source)
                .Cast<Match>()
                .Select(m => m.Value);
            return lines;
        }
    }
}
